from __future__ import annotations
import time
import tracemalloc
from dataclasses import dataclass
from typing import Any, Callable, Optional

@dataclass
class RunMeasure:
    ok: bool
    value: Any = None
    error: Optional[str] = None
    seconds: float = 0.0
    peak_mb: float = 0.0

def run_and_measure(fn: Callable, *args, **kwargs) -> RunMeasure:
    tracemalloc.start()
    t0 = time.perf_counter()
    try:
        val = fn(*args, **kwargs)
        ok = True
        err = None
    except Exception as e:
        val = None
        ok = False
        err = f"{type(e).__name__}: {e}"
    seconds = time.perf_counter() - t0
    current, peak = tracemalloc.get_traced_memory()
    tracemalloc.stop()
    peak_mb = peak / (1024*1024)
    return RunMeasure(ok=ok, value=val, error=err, seconds=seconds, peak_mb=peak_mb)

def html_escape(s: str) -> str:
    import html as _html
    return _html.escape(s, quote=True)

def safe_exec(code: str) -> dict:
    ns = {"__name__": "__cab_eval__"}
    exec(compile(code, "<problem_code>", "exec"), ns, ns)
    return ns

def linear_regression(xs, ys):
    n = len(xs)
    if n == 0:
        return 0.0, 0.0
    sx = sum(xs)
    sy = sum(ys)
    sxx = sum(x*x for x in xs)
    sxy = sum(x*y for x,y in zip(xs, ys))
    denom = n*sxx - sx*sx
    if denom == 0:
        return 0.0, sy / n if n else 0.0
    slope = (n*sxy - sx*sy)/denom
    intercept = (sy - slope*sx)/n
    return slope, intercept

def score_from_time(t: float, tref: float) -> float:
    if t < 0:
        return 0.0
    return 100.0 / (1.0 + (t / max(1e-9, tref)))

def score_from_memory(mb: float, mref: float) -> float:
    return 100.0 / (1.0 + (mb / max(1e-9, mref)))

def clamp01(x: float) -> float:
    return max(0.0, min(1.0, x))
